package tech.edwardvan.springsecuritydemo.security.config;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.expression.WebExpressionVoter;
import tech.edwardvan.springsecuritydemo.rbac.service.impl.RbacUserDetailServiceImpl;

import java.util.ArrayList;
import java.util.List;

/**
 * 前后端不分离Security 配置
 *
 * @author EdwardVan
 */
@Configuration
@RequiredArgsConstructor
public class WebSecurityConfig {

    private final RbacUserDetailServiceImpl rbacUserDetailService;

    /**
     * anyRequest          |   匹配所有请求路径
     * access              |   SpringEl表达式结果为true时可以访问
     * anonymous           |   匿名可以访问
     * denyAll             |   用户不能访问
     * fullyAuthenticated  |   用户完全认证可以访问(非remember-me下自动登录)
     * hasAnyAuthority     |   如果有参数,参数表示权限,则其中任何一个权限可以访问
     * hasAnyRole          |   如果有参数,参数表示角色,则其中任何一个角色可以访问
     * hasAuthority        |   如果有参数,参数表示权限,则其权限可以访问
     * hasIpAddress        |   如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
     * hasRole             |   如果有参数,参数表示角色,则其角色可以访问
     * permitAll           |   用户可以任意访问
     * rememberMe          |   允许通过remember-me登录的用户访问
     * authenticated       |   用户登录后可访问
     */
    @Bean
    @Order(2)
    SecurityFilterChain webSiteSecurityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeRequests(requests ->
                        requests
                                .antMatchers("/login").permitAll()//任何用户都可以访问的URL
                                .antMatchers("/web/log").hasAnyAuthority("web:log")
                                .antMatchers("/web/limit").hasAnyAuthority("web:limit")
                                .anyRequest().authenticated()//没匹配的接口都需要进行验证
                                .accessDecisionManager(accessDecisionManager())//自定义accessDecisionManager
                )
                // 启用表单登录
                .formLogin(
                        formLogin -> formLogin
                                .loginPage("/login.html")
                                .loginProcessingUrl("/login")
                )
                .userDetailsService(rbacUserDetailService)
                .csrf(csrf -> csrf.disable());
        return http.build();
    }

    /**
     * 自定义AccessDecisionManager
     */
    private AccessDecisionManager accessDecisionManager() {
        return new AffirmativeBased(getDecisionVoters());
    }

    /**
     * 自定义AccessDecisionVoter
     */
    List<AccessDecisionVoter<?>> getDecisionVoters() {
        List<AccessDecisionVoter<?>> decisionVoters = new ArrayList<>();
        WebExpressionVoter expressionVoter = new WebExpressionVoter();
        RoleVoter roleVoter = new RoleVoter();
        decisionVoters.add(expressionVoter);
        decisionVoters.add(roleVoter);
        return decisionVoters;
    }

}
